#include "client.h"
#include "ui_client.h"

#include <QThread>

Client::Client(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::Client), mSocket(new QTcpSocket(this))
{
    ui->setupUi(this);
    ui->listWidgetFiles->setVerticalScrollMode(QListWidget::ScrollPerPixel);

    // 连接时发送消息进行Sha256验证
    connect(mSocket, &QTcpSocket::connected, [this] {
        QString verify =
                ui->editPassword->text() +
                QString::number(mSocket->localAddress().toIPv4Address()) +
                QString::number(mSocket->localPort());
        addSocketQueue(new SocketQueue_NormalMsg(Sha256(verify.toUtf8()) + ui->editName->text().toUtf8()));
        ui->editIP->setText(mSocket->peerAddress().toString());
        ui->editPort->setText(QString::number(mSocket->peerPort()));
        ui->btnConnect->setText("断开");

        ui->editName->setEnabled(false);
        ui->editIP->setEnabled(false);
        ui->editPort->setEnabled(false);
        ui->editPassword->setEnabled(false);
        ui->fileManager->setEnabled(true);

        connect(mSocket, &QTcpSocket::readyRead, this, &Client::onTcpReadyRead);
    });
    // 断开连接时进行相关操作
    connect(mSocket, &QTcpSocket::disconnected, [this] {
        ui->btnConnect->setText("连接");

        ui->editName->setEnabled(true);
        ui->editIP->setEnabled(true);
        ui->editPort->setEnabled(true);
        ui->editPassword->setEnabled(true);
        ui->fileManager->setEnabled(false);
        ui->fileManager->listWidget()->clear();
        ui->fileManager->editPath()->clear();
        for(SocketQueue *sq : mSocketQueueList)
            delete sq;
        mSocketQueueList.clear();
        serverDir = "";

        disconnect(mSocket, &QTcpSocket::readyRead, this, &Client::onTcpReadyRead);
    });
    // 发送完成检查是否还有数据待发送
    connect(mSocket, &QTcpSocket::bytesWritten, [this] {
        SocketQueue *sq = mSocketQueueList.takeAt(0);
        if(sq->isFinished()) {
            delete sq;
        } else mSocketQueueList << sq;

        writeFirstSocketQueue();
    });
    // 刷新文件列表
    connect(ui->fileManager, &FileManager::flush, [this] {
        writeDirRequest(serverDir);
    });
    // 返回主列表
    connect(ui->fileManager, &FileManager::mainlist, [this] {
        writeDirRequest("");
    });
    // 父目录
    connect(ui->fileManager, &FileManager::parentdir, [this] {
        writeDirRequest(serverDir, "Parent");
    });
    // 上传文件
    connect(ui->fileManager, &FileManager::upload, this, &Client::onUpload);
    // 进入目录
    connect(ui->fileManager->listWidget(), &QListWidget::itemDoubleClicked, [this](QListWidgetItem *item) {
        if(!item->data(Qt::UserRole).toBool())
            return;
        QString send = serverDir.endsWith('/') || serverDir.isEmpty() ? serverDir + item->text() : serverDir + '/' + item->text();
        writeDirRequest(send);
    });

    // 读取配置信息
    QSettings config(APP_DIR + "/config.ini", QSettings::IniFormat);
    ui->editName->setText(config.value("Client/Name").toString());
    ui->editIP->setText(config.value("Client/S_IP").toString());
    ui->editPort->setText(config.value("Client/S_Port").toString());
    ui->editPassword->setText(config.value("Client/S_Password").toString());
}

void Client::closeEvent(QCloseEvent *) {
    // 断开连接
    if(mSocket->state() != QTcpSocket::UnconnectedState)
        mSocket->disconnectFromHost();

    // 写入配置信息
    QSettings config(APP_DIR + "/config.ini", QSettings::IniFormat);
    config.setValue("Client/Name", ui->editName->text());
    config.setValue("Client/S_IP", ui->editIP->text());
    config.setValue("Client/S_Port", ui->editPort->text());
    config.setValue("Client/S_Password", ui->editPassword->text());
}

void Client::writeDirRequest(const QString &path, const QString &sp) {
    // 使用XML向服务端发送DirRequest
    QByteArray xml;
    QXmlStreamWriter xmlWriter(&xml);
    xmlWriter.writeStartDocument();
    xmlWriter.writeStartElement("DirRequest");
    xmlWriter.writeAttribute("Path", path);
    xmlWriter.writeAttribute("Sp", sp);
    xmlWriter.writeEndElement();
    xmlWriter.writeEndDocument();
    addSocketQueue(new SocketQueue_NormalMsg(xml));
}
void Client::parseReceiveMsg(const QByteArray &array) {
    // 文件图标
    static QMap<QString, QFileIconProvider::IconType> standardFileIcons;
    if(standardFileIcons.isEmpty()) {
        standardFileIcons["Driver"] = QFileIconProvider::Drive;
        standardFileIcons["Desktop"] = QFileIconProvider::Desktop;
        standardFileIcons["Home"] = QFileIconProvider::Folder;
        standardFileIcons["Dir"] = QFileIconProvider::Folder;
    }

    ReceiveData data = parseReceiveData(array);

    QDomDocument dom;
    if(!dom.setContent(data.xml))
        return;
    QDomElement root = dom.documentElement();
    // 如果消息为FileList，则解析并显示
    // 如果消息为SendFileRequest，则传输文件数据
    if(root.tagName() == "FileList") {  // 文件列表
        serverDir = root.attribute("Path");
        ui->fileManager->editPath()->setText(serverDir);
        ui->fileManager->listWidget()->setCurrentRow(0);
        ui->fileManager->listWidget()->clear();

        // 遍历所有节点
        QDomNode node = root.firstChild();
        while(!node.isNull()) {
            QDomElement elem = node.toElement();
            if(!elem.isNull()) {
                QString name = elem.nodeName(), text = elem.text();
                QListWidgetItem *item = new QListWidgetItem(text);
                item->setFlags(Qt::ItemIsEnabled);
                item->setData(Qt::UserRole, name != "File");
                if(name == "File") {
                    item->setIcon(QFileIconProvider().icon(text));
                } else {
                    auto iter = standardFileIcons.constFind(name);
                    if(iter != standardFileIcons.cend())
                        item->setIcon(QFileIconProvider().icon(*iter));
                }
                ui->fileManager->listWidget()->addItem(item);
            }
            node = node.nextSibling();
        }
    } else if(root.tagName() == "SendFileRequest") {
        QString clientFilePath = root.attribute("ClientPath");
        QString serverFilePath = root.attribute("ServerPath");
        if(!clientFilePath.isEmpty() && !serverFilePath.isEmpty()) {
            SocketQueue_FileMsg *sq = new SocketQueue_FileMsg(clientFilePath, serverFilePath, &serverDir);
            addSocketQueue(sq);

            QListWidgetItem *item = new QListWidgetItem;
            item->setFlags(Qt::ItemFlag::ItemIsEnabled);
            SendFileWidget *widget = new SendFileWidget("发送中", QFileInfo(clientFilePath).fileName(), sq->size());
            widget->adjustSize();
            item->setSizeHint(QSize(1, widget->height()));
            ui->listWidgetFiles->insertItem(0, item);
            ui->listWidgetFiles->setItemWidget(item, widget);
            connect(sq, &SocketQueue_FileMsg::progress, widget, &SendFileWidget::setProgress);
            connect(sq, &SocketQueue_FileMsg::sendFileFinished, [this, item, widget, serverFilePath] {
                widget->setProgress(widget->total());
                widget->setMsg("校验中");
                widget->setProgressBarVisible(false);
                widget->adjustSize();
                item->setSizeHint(QSize(1, widget->height()));
                mMapPathToWidget[serverFilePath] = widget;
            });
        }
    } else if(root.tagName() == "ChecksumResult") {
        QString filePath = root.attribute("Path");
        bool isRight = root.text().toInt();
        SendFileWidget *widget = mMapPathToWidget.take(filePath);
        if(widget)
            widget->setMsg(isRight ? "校验成功" : "校验失败");
    }
}

void Client::onTcpReadyRead() {
    mSocketBuffer += mSocket->readAll();

    if(mSocketBuffer.length() < 8)
        return;
    qint64 len = *reinterpret_cast<qint64*>(mSocketBuffer.data());
    while(len <= mSocketBuffer.length() - 8) {
        parseReceiveMsg(mSocketBuffer.mid(8, (int)len));
        mSocketBuffer = mSocketBuffer.mid(8 + (int)len);
        if(mSocketBuffer.length() < 8)
            break;
        len = *reinterpret_cast<qint64*>(mSocketBuffer.data());
    }
}
void Client::onUpload() {
    if(serverDir.isEmpty()) {
        QMessageBox::information(this, "提示", "无法上传至主列表");
        return;
    }
    QSettings config(APP_DIR + "/config.ini", QSettings::IniFormat);
    QStringList list = QFileDialog::getOpenFileNames(this, "上传文件", config.value("Client/UploadPath").toString());
    if(list.isEmpty())
        return;
    config.setValue("Client/UploadPath", QFileInfo(list[0]).path());

    QByteArray array;
    QXmlStreamWriter xmlWriter(&array);
    xmlWriter.writeStartDocument();
    xmlWriter.writeStartElement("PreSendFile");
    xmlWriter.writeAttribute("Path", serverDir);
    for(const QString &filePath : list) {
        xmlWriter.writeStartElement("File");
        xmlWriter.writeAttribute("Size", QString::number(QFile(filePath).size()));
        xmlWriter.writeCharacters(filePath);
        xmlWriter.writeEndElement();
    }
    xmlWriter.writeEndElement();
    xmlWriter.writeEndDocument();
    addSocketQueue(new SocketQueue_NormalMsg(array));
}

void Client::on_btnConnect_clicked()
{
    if(mSocket->state() == QTcpSocket::UnconnectedState) {
        mSocket->connectToHost(QHostAddress(ui->editIP->text()), ui->editPort->text().toUShort());
        return;
    }
    mSocket->disconnectFromHost();
}

void Client::addSocketQueue(SocketQueue *sq) {
    if(!sq->isVaild())
        return;
    mSocketQueueList << sq;
    if(mSocketQueueList.size() == 1)
        writeFirstSocketQueue();
}
void Client::writeFirstSocketQueue() {
    if(mSocketQueueList.isEmpty())
        return;
    QByteArray data = mSocketQueueList[0]->data();
    qint64 len = data.length();
    mSocket->write(QByteArray((char*)&len, 8) + data);
}
